#pragma once

#include "MyControls/AsyncSequenceView.h"
#include "MyControls/Sequence.h"
#include "MyControls/Resource.h"
#include <imglib/winimage.h>

namespace My {

template <typename T>
inline AsyncSequenceView<T>::AsyncSequenceView<T>()
{
	mswin::synchronizable_thread::start();
	SetThreadPriority(handle(), THREAD_PRIORITY_BELOW_NORMAL);
}

template <typename T>
inline AsyncSequenceView<T>::~AsyncSequenceView<T>()
{
	mswin::synchronizable_thread::terminate();
}

template <typename T>
void AsyncSequenceView<T>::HandleKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (nChar == VK_SPACE && ::GetAsyncKeyState(VK_MENU) == 0) {
		//if (m_Editor.IsOpen())
		//	SequenceView::HandleKeyDown(nChar, nRepCnt, nFlags); //fed: changed from HandleKeyUp() - I thought it was a bug
		//else
			AfxGetMainWnd()->SendMessage(WM_COMMAND, ID_TIME_PLAYPAUSEFORWARD);
	} else
		SequenceView::HandleKeyDown(nChar, nRepCnt, nFlags); //fed: changed from HandleKeyUp() - I thought it was a bug
}

template <typename T>
void AsyncSequenceView<T>::OnTimer(UINT nIDEvent)
{
	//Is previous frame already painted?
	if (mswin::synchronizable_thread::idle()) {
		m_PainterWasNotInTime = false;
		SequenceView::OnTimer(nIDEvent);
	} else {
		m_PainterWasNotInTime = true;
		ZoomBench::OnTimer(nIDEvent);
	}
}

template <typename T>
typename AsyncSequenceView<T>::BufferSPtr AsyncSequenceView<T>::GetBufferForDisplaying()
{
	BufferSPtr BufferCopy = m_Buffer; //m_Buffer may change every moment by painter thread
	if (BufferCopy && !m_BufferOnceDisplayed) {
		m_BufferOnceDisplayed = true;
		CalcRealFramerate();
		if (m_PainterWasNotInTime && IsPlaying()) {
			StopTimer();
			StartTimer();
			SequenceView::OnTimer(0); // 0 is not absolutely correct
			m_PainterWasNotInTime = false;
		}
	}
	return BufferCopy;
}

template <typename T>
void AsyncSequenceView<T>::PaintImageRect(CDC * pDC, CRect const & rClient, CRect const & rImage)
{
	BufferSPtr BufferCopy = GetBufferForDisplaying();
	if (!BufferCopy) {
		CBrush bk(GetBackgroundColor());
		pDC->FillRect(&rClient, &bk);
		return;
	}

	pDC->SetStretchBltMode(HALFTONE);
	img::StretchImage(pDC->m_hDC, 
		rClient.left, rClient.top, rClient.Width(), rClient.Height(),
		rImage.left,  rImage.top,  rImage.Width(),  rImage.Height(), 
		BufferCopy->Image->im, SRCCOPY);
}

template <typename T>
void AsyncSequenceView<T>::report_error(const char * msg)
{
	AfxMessageBox(msg, MB_OK | MB_ICONEXCLAMATION);
}

template <typename T>
void AsyncSequenceView<T>::CalcRealFramerate()
{
	My::TimeControls * tc = GetTimeControls();

	if (!IsPlaying()) {
		if (tc)
			tc->ClearRealFramerate();
		return;
	}

	m_FPS.Frame();

	if (tc)
		m_FPS.SetFramerate(*tc);
}

template <typename T>
inline typename AsyncSequenceView<T>::PaintingKeySPtr 
	AsyncSequenceView<T>::GetCurrentPaintingKey()
{
	return GetPaintingKey(GetSequence()->GetPreviewFrame());
}

template <typename T>
void AsyncSequenceView<T>::doit()
{
	mswin::thread::set_debug_name("Painter (AsyncSequenceView)");

	//m_Order may be changed by main thread at any time
	PaintingKeySPtr order = m_Order; 
	if (!order)
		return;

	//ChangeCursor
	icache::nearby n;
	switch (GetPlayMode()) {
		case My::PlayForward:
			n = icache::forward;
			break;
		case My::PlayBackward:
			n = icache::backward;
			break;
		default:
			n = icache::no_nearby;
	}
	CachedImageSPtr i = GetPainting(order, n);
	assert (i);

	//show the painting only if the order was not changed (save time and eliminate flicker)
	if (order == m_Order) { //pointer comparison is ok here
		/// replace current buffer and invalidate bench
		BufferSPtr NewBuffer(new Buffer);
		NewBuffer->Key = order;
		NewBuffer->Image = i;
		m_Buffer.swap(NewBuffer);
		m_BufferOnceDisplayed = false;
		InvalidateBench();
	}
}

template <typename T>
void AsyncSequenceView<T>::InvalidateBuffer()
{
	if (!GetSequence() || GetSequence()->IsEmpty())
		return;
	PaintingKeySPtr p(GetCurrentPaintingKey());

	/*	Pasha: if no buffered image is required, we
		simply reset the buffer */
	if (!p) {
		bool BufferWasFull = m_Buffer;
		EraseBuffer();
		if (BufferWasFull)
			InvalidateBench();
		return;
	}

	//BufferUpToDate(p)?
	CacheKeySPtr BufId(GetBufferKey());
	if (BufId && p->equal(*BufId)) {
		m_Order.reset();
		return;
	}

	//Order already processing?
	if (m_Order && p->equal(*m_Order))
		return;

	m_Order = p;
	mswin::synchronizable_thread::repeat(); //repeat painting
}

template <typename T>
void AsyncSequenceView<T>::EraseBuffer()
{
	m_Buffer.reset();
	m_Order.reset();
}

} //namespace My
